package com.woniuxy.microfundservice.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.woniuxy.bean.BillBean;
import com.woniuxy.bean.ResultBean;
import com.woniuxy.bean.regularbean.MemberAccountInnerBean;
import com.woniuxy.bean.regularbean.MemberAccountOutBean;
import com.woniuxy.microfundservice.bean.MemberFundAccount;
import com.woniuxy.microfundservice.bean.info.FundFullInfo;
import com.woniuxy.microfundservice.dao.*;
import com.woniuxy.microfundservice.fegin.OrderFegin;
import com.woniuxy.microfundservice.fegin.SysFegin;
import com.woniuxy.microfundservice.service.MemberFundAccountService;
import com.woniuxy.microfundservice.util.ErrorEntity;
import com.woniuxy.microfundservice.util.MD5;
import org.joda.time.DateTime;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

import static com.woniuxy.microfundservice.util.ErrorEntity.SHARE_LACKING;

/**
 * (MemberFundAccount)表服务实现类
 *
 * @author makejava
 * @since 2021-04-12 19:11:45
 */
@Service("memberFundAccountService")
public class MemberFundAccountServiceImpl implements MemberFundAccountService {
    @Resource
    private MemberFundAccountDao memberFundAccountDao;
    @Resource
    private FundFullInfoDao fundFullInfoDao;
    @Resource
    private ListOperations listOperations;
    @Resource
    private WorkDayDao workDayDao;
    @Resource
    private MemberDao memberDao;
    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private OrderDao orderDao;
    @Resource
    private OrderFegin orderFegin;
    @Resource
    private SysFegin sysFegin;

    @Resource
    private final ObjectMapper om=new ObjectMapper();


    @Override
    public MemberAccountOutBean selectByUser(String userName) {

        //获取用户购买基金列表
        List<MemberFundAccount> memberFundAccounts = memberFundAccountDao.selectByUser(userName);
        if (memberFundAccounts.size() < 1) {
            return new MemberAccountOutBean();
        }
        MemberAccountInnerBean innerBean = new MemberAccountInnerBean();
        List<MemberAccountInnerBean> innerBeans = new ArrayList<>();
        //所有购买的产品总收益
        double sumEarnings = 0.0;
        //所有购买的产品总价值
        double sumOverAllBalance = 0.0;
        //所有今日收益
        double sumTodayEarnings=0.0;

        FundFullInfo fundFullInfo=new FundFullInfo();
        //将列表信息放进返回信息中
        for (MemberFundAccount account : memberFundAccounts) {
            fundFullInfo=fundFullInfoDao.selectById(account.getFundId());
            //计算所有购买的产品总收益
            sumEarnings += account.getTotalEarnings();
            //计算所有购买的产品总价值
            sumOverAllBalance += account.getOverAllBalance();
            //计算今日总收益
            sumTodayEarnings +=account.getTodayEarnings();

            innerBean.setAid(account.getId());
            innerBean.setMemberAccount(account.getMemberAccount());
            innerBean.setFixedDepositId(account.getFundId());
            innerBean.setOverAllBalance((double) Math.round(account.getOverAllBalance() * 100) / 100 );
            innerBean.setTodayEarnings((double) Math.round(account.getTodayEarnings()  * 100) / 100);
            innerBean.setTotalEarnings((double) Math.round(account.getTotalEarnings() * 100) / 100);
            innerBean.setStatus(account.getStatus());
            innerBean.setProductName(fundFullInfo.getFundName());
            innerBean.setShare((double) Math.round(account.getShare()  * 100) / 100);
            //将返回信息放进列表中
            innerBeans.add(innerBean);
        }
        //将返回信息放进外层实体类中
        MemberAccountOutBean outBean = new MemberAccountOutBean();
        outBean.setMemberAccountInnerBeans(innerBeans);
        //限制小数点位数
        sumEarnings= (double) Math.round(sumEarnings * 100) / 100;
        sumOverAllBalance= (double) Math.round(sumOverAllBalance * 100) / 100;
        sumTodayEarnings= (double) Math.round(sumTodayEarnings * 100) / 100;
        //存进返回实体类中
        outBean.setSumEarnings(sumEarnings);
        outBean.setSumOverAllBalance(sumOverAllBalance);
        outBean.setTodaySumEarnings(sumTodayEarnings);
        return outBean;
    }

    @Override
    public int addOne(BillBean billBean) {
        return memberFundAccountDao.addOne(billBean);
    }

    @Override
    public MemberFundAccount selectExit(String memberAccount, Integer fundId) {
        return memberFundAccountDao.selectExit(memberAccount, fundId);
    }

    @Override
    public int increase(BillBean billBean) {
        return memberFundAccountDao.increase(billBean);
    }

    @Override
    public int updateBySell(MemberFundAccount memberFundAccount) {
        return memberFundAccountDao.sellThen(memberFundAccount);
    }

    //获取该基金所有购买日期
    @Override
    public List<BillBean> selectorder(String memberAccount, int fundId) {
        return orderDao.selectorder(memberAccount, fundId);
    }

    //计算赎回费率
    @Override
    public Double rate(String memberAccount, int fundId, double share) {
        //从数据库查询该基金购买日期
        List<BillBean> orderBeans = selectorder(memberAccount, fundId);
        long purchaseDate = 0L;
        Date temp = new Date();
        //遍历订单数据
        for (BillBean o : orderBeans) {
            share = share - o.getShare();
            //份额足够扣
            if (share <= 0) {
                //获取最早购买时间作为赎回费率判断依据
                temp = o.getCreateTime();
                purchaseDate = temp.getTime();
                break;
            }
        }
        if (purchaseDate == 0) {
            //未获取到购买时间
        }
        Date nowTime = new Date();
        long nowDate = nowTime.getTime();
        long holdTime = nowDate - purchaseDate;
        long seven = 1000 * 60 * 60 * 24 * 7L;
        long oneyear = 1000 * 60 * 60 * 24 * 365L;
        long towyear = 1000 * 60 * 60 * 24 * 365 * 2L;
        double rate = -1.0;
        if (holdTime <= seven) {
            //持有时间小于7天
            rate = 1.0;
        } else if (holdTime <= oneyear && holdTime > seven) {
            //持有时间小于一年大于七天
            rate = 0.5;
        } else if (holdTime <= towyear && holdTime > oneyear) {
            //持有时间大于一年小于两年
            rate = 0.25;
        } else if (holdTime > towyear) {
            //持有时间大于两年
            rate = 0.0;
        }
        return rate;
    }

    @Override
    public void creatOrder(BillBean billBean, String orderType) {
        billBean.setOrderType(orderType);
        billBean.setProductType("基金");
        orderFegin.createFakeOrder(billBean);
    }

    @Override
    public void updateDaily(FundFullInfo newInfos) {
        memberFundAccountDao.updateDaily(newInfos);
    }

    //购买时计算下订单日期，放进redis
    @Override
    public ResultBean sureOrderDate(BillBean billBean) {
        //购买扣费
        String s = sysFegin.balancePayment(billBean);
        ResultBean resultBean = JSONObject.parseObject(s, ResultBean.class);
        int code = resultBean.getCode();
        //判断余额是否足够
        if(code!=200){
            return resultBean;
        }
        //获取当前购买时间
        int purchaseHoure = new DateTime().getHourOfDay();
        //获取购买日期
        String purchaseDateStr = new DateTime().toString("yyyyMMdd");
        int purchaseDate = Integer.parseInt(purchaseDateStr);
        //购买后确定份额工作日
        int ensureDate = 0;
        List<Integer> secondDate=new ArrayList<>();
        if (purchaseHoure < 15) {
            //当天下午三点前购买
            //获取购买后确定份额日期
            ensureDate = workDayDao.getAfterWorkDates(purchaseDate);
            String ensureDateStr = ensureDate + "Buy";
            //存进list，key为确定份额日期
            listOperations.leftPush(ensureDateStr, billBean);
        } else {
            //三点后购买
            //确定份额日期
            secondDate = workDayDao.getSecondWorkDates(purchaseDate);
            String ensureDateStr = secondDate.get(1) + "Buy";
            listOperations.leftPush(ensureDateStr, billBean);
        }

        return ResultBean.builder().code(200).msg("购买成功").build();
    }

    //购买后确定份额定时任务
    @Override
    @Scheduled(cron="0 10 17 ? * *")
    public void workDayPurchase(){
        //获取当前日期
//        String purchaseDateStr = new DateTime().toString("yyyyMMdd")+"Buy";
        String purchaseDateStr ="20210427Buy";
        //将redis中缓存的待确定订单取出生成订单
        List<LinkedHashMap> range =listOperations.range(purchaseDateStr, 0, -1);
        range.forEach(billbean->{
            BillBean billBean = om.convertValue(billbean, BillBean.class);
            payThen(billBean);
        });
        redisTemplate.delete(purchaseDateStr);
    }

    //判断购买份额
    public ResultBean payThen(BillBean billBean) {
        //该基金当前信息
        FundFullInfo fundFullInfo = fundFullInfoDao.selectOne(billBean.getProductId());
        //基金当日净值
        String netValue = fundFullInfo.getNetValue();
        double value = Double.parseDouble(netValue);
        //购买份数的金额  花费金额/(1+买入费率)
        double money=billBean.getModifiedPrice();
        //购买的份数  购买金额/当日净值
        double share=money/value;
        billBean.setShare(share);
        //判断是否第一次购买
        MemberFundAccount memberFund=selectExit(billBean.getMemberName(),billBean.getProductId());
        if(memberFund==null){
            //没有数据，第一次购买，新增
            int res=addOne(billBean);
        }else {
            //有数据，加仓，修改
            int res=increase(billBean);
        }
        //创建订单
        creatOrder(billBean,"购买");
        //增加该基金推荐值
        fundFullInfoDao.addScore(billBean.getProductId());
        return ResultBean.builder().code(200).msg("购买成功").data(billBean).build();
    }

    //赎回时计算下订单日期，放进redis
    @Override
    public ResultBean sureSellDate(BillBean billBean){
        //验证支付密码
        String oldPayPassword = memberDao.selectByAccount(billBean.getMemberName());
        //余额支付
        if (billBean.getPayType().contains(ErrorEntity.BALANCE) && oldPayPassword == null) {
            //支付密码为空：未实名
            return ResultBean.builder().code(607).msg(ErrorEntity.NO_REAL_NAME).build();
        }
        if (billBean.getPayType().contains(ErrorEntity.BALANCE) && ! oldPayPassword.equals(MD5.encrypt(billBean.getPayPassword()))) {
            //支付密码错误
            return ResultBean.builder().code(607).msg(ErrorEntity.WRONG_PASSWORD).build();
        }
        //银行卡支付
        if (billBean.getPayType().contains(ErrorEntity.BANKCARD)) {
            String bankPsw = memberDao.selectByid(billBean.getBankCardId());
            if(! billBean.getPayPassword().equals(bankPsw)){
                //支付密码错误
                return ResultBean.builder().code(607).msg(ErrorEntity.WRONG_PASSWORD).build();
            }
        }
        //获取修改基金 联系人的详细信息
        MemberFundAccount memberInfo=selectExit(billBean.getMemberName(),billBean.getProductId());
        if(memberInfo==null){
            return ResultBean.builder().code(707).msg("该用户未购买该基金").build();
        }
        //判断是否有足量份额可供出售
        if(billBean.getShare()>memberInfo.getShare()){
            return ResultBean.builder().code(707).msg(SHARE_LACKING).build();
        }
        //获取当前购买时间
        int hourOfDay = new DateTime().getHourOfDay();
        //获取购买日期
        String purchaseDateStr=new DateTime().toString("yyyyMMdd");
        int purchaseDate=Integer.parseInt(purchaseDateStr);
        Integer ensureDay =0;
        List<Integer> secondDate=new ArrayList<>();
        //判断是否在三点前赎回
        if(hourOfDay<15){
            //下午三点前赎回
            ensureDay = workDayDao.getAfterWorkDates(purchaseDate);
            //获取确定份额的工作日
            String enusreDayStr=ensureDay+"sell";
            //存进redis
            listOperations.leftPush(enusreDayStr,billBean);
        }else {
            //三点后购买
            secondDate = workDayDao.getSecondWorkDates(purchaseDate);
            //确定份额的工作日
            String enusreDayStr=secondDate.get(1)+"sell";
            //存进redis
            listOperations.leftPush(enusreDayStr,billBean);
        }
        //减去份额
        memberFundAccountDao.reduceShare(billBean);
        return ResultBean.builder().code(200).msg("赎回成功").build();
    }

    //
    //赎回后确定份额定时任务
    @Scheduled(cron="0 10 17 ? * *")
    @Override
    public void workDaySell(){
        //获取当前日期
//        String purchaseDateStr = new DateTime().toString("yyyyMMdd")+"sell";
        String purchaseDateStr ="20210427sell";
        List<LinkedHashMap> range =listOperations.range(purchaseDateStr, 0, -1);
        range.forEach(billbean->{
            BillBean billBean = om.convertValue(billbean, BillBean.class);
            sellThen(billBean);
        });
        redisTemplate.delete(purchaseDateStr);
    }

    public ResultBean sellThen(BillBean billBean) {
        //获取修改基金 联系人的详细信息
        MemberFundAccount memberInfo=selectExit(billBean.getMemberName(),billBean.getProductId());
            //份额足够
            // 查找该基金当前信息
            FundFullInfo fundFullInfo = fundFullInfoDao.selectOne(billBean.getProductId());
            //当日净值
            String netValue = fundFullInfo.getNetValue();
            double value = Double.parseDouble(netValue);
            //赎回金额   当日净值*赎回份额
            double redeem=value*billBean.getShare();
            //得到的金额  赎回金额(1-赎回费率)
        String[] split = fundFullInfo.getSellRate().split("%");
        double getMoney=redeem*(1-rate(billBean.getMemberName(),billBean.getProductId(),billBean.getShare())
                *Double.parseDouble(split[0]));
            billBean.setModifiedPrice(-getMoney);
            //平均净值   购买该基金总成本/该基金总份额
            double averCost=memberInfo.getTotalMoney()/(memberInfo.getShare()+billBean.getShare());
            //总花费需减去的成本  平均净值*赎回份额
            double cost=averCost*billBean.getShare();
            MemberFundAccount modify=new MemberFundAccount();
            //总成本要减去的金额
            modify.setTotalMoney(cost);
            //要减去的份额数
            modify.setShare(billBean.getShare());
            //要减去的总价值
            modify.setOverAllBalance(getMoney);
            //修改数据库
            int res=updateBySell(modify);
            //本次收益
            double earn=getMoney-cost;
        //生成订单
        creatOrder(billBean,"赎回");
        //将订单信息存进redis，金额两天后到账
        //获取购买日期
        String purchaseDateStr=new DateTime().toString("yyyyMMdd");
        int purchaseDate=Integer.parseInt(purchaseDateStr);
        //获取到款时间
        List<Integer> secondDate = workDayDao.getSecondWorkDates(purchaseDate);
        //确定到款的工作日
        String enusreDayStr=secondDate.get(1)+"getMoney";
        //存进redis
        listOperations.leftPush(enusreDayStr,billBean);
        //减去份额和成本
        memberFundAccountDao.reduceMoney(billBean.getMemberName(),billBean.getProductId(),redeem,cost);
        return ResultBean.builder().code(200).msg("赎回成功").data(billBean).build();
    }

    //到款定时任务
    @Override
    @Scheduled(cron="0 20 17 ? * *")
    public void getMoney(){

        //获取当前日期
        String purchaseDateStr = new DateTime().toString("yyyyMMdd")+"getMoney";
//        String purchaseDateStr ="20210425getMoney";
        List<LinkedHashMap> range =listOperations.range(purchaseDateStr, 0, -1);
        range.forEach(billbean->{
            BillBean billBean = om.convertValue(billbean, BillBean.class);
            //到款
            sysFegin.balancePayment(billBean);
        });
        redisTemplate.delete(purchaseDateStr);
    }
}